/*
 * Copyright Eclub organization.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

 * http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.eclub.emotion.rootstone.serializer.scanning;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.NoSuchElementException;
import java.util.Stack;

import com.eclub.emotion.rootstone.server.ResourceFinder;
import com.eclub.emotion.rootstone.util.Tokenizer;

public class FilesScanner implements ResourceFinder{
	
	 private ResourceFinderStack resourceFinderStack = new ResourceFinderStack();

	/**
     * Scan from a set of packages.
     *
     * @param files:containing package names.
     */
    public FilesScanner(final String files) {
        this(new String[]{files});
    }

    private final File[] files;

    /**
     * Scan from a set of packages.
     *
     * @param fileNames an array of package names.
     */
    public FilesScanner(final String[] fileNames) {
        files = new File[Tokenizer.tokenize(fileNames, Tokenizer.COMMON_DELIMITERS).length];
        for (int i = 0; i < files.length; i++) {
            files[i] = new File(fileNames[i]);
        }

        for(final File f : files) {
            processFile(f);
        }
    }

    private void processFile(final File f) {

        if(f.getName().endsWith(".jar") || f.getName().endsWith(".zip")) {
            try {
                resourceFinderStack.push(new JarFileScanner(new FileInputStream(f), ""));
            } catch(IOException e) {
                // logging might be sufficient in this case
                throw new ResourceFinderException(e);
            }

        } else {
            resourceFinderStack.push(new ResourceFinder() {

                Stack<File> files = new Stack<File>() {{
                    if(f.isDirectory()) {
                        for(File file : f.listFiles()) {
                            push(file);
                        }
                    } else {
                        push(f);
                    }
                }};

                private File current;
                private File next;

                @Override
                public boolean hasNext() {
                    while(next == null && !files.empty()) {
                        next = files.pop();

                        if(next.isDirectory() || next.getName().endsWith(".jar") || next.getName().endsWith(".zip")) {
                            processFile(next);
                            next = null;
                        }
                    }

                    return next != null;
                }

                @Override
                public String next() {
                    if(next != null || hasNext()) {
                        current = next;
                        next = null;
                        return current.getName();
                    }
                    throw new NoSuchElementException();
                }

                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }

                @Override
                public InputStream open() {
                    try {
                        return new FileInputStream(current);
                    } catch (FileNotFoundException e) {
                        throw new ResourceFinderException(e);
                    }
                }

                @Override
                public void reset() {
                }
            });
        }
    }

    @Override
    public boolean hasNext() {
        return resourceFinderStack.hasNext();
    }

    @Override
    public String next() {
        return resourceFinderStack.next();
    }

    @Override
    public void remove() {
        resourceFinderStack.remove();
    }

    @Override
    public InputStream open() {
        return resourceFinderStack.open();
    }

    @Override
    public void reset() {
        this.resourceFinderStack = new ResourceFinderStack();

        for(File f : files) {
            processFile(f);
        }
    }

}


